package com.threegroup.web.service;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.threegroup.common.dao.OrderDetailDao;
import com.threegroup.common.dao.OrdersDao;
import com.threegroup.common.dao.ShoppingCartDao;
import com.threegroup.common.dto.OrderDetailDTO;
import com.threegroup.common.dto.OrdersDTO;
import com.threegroup.common.dto.OrdersPageDTO;
import com.threegroup.common.dto.OrdersQueryDTO;
import com.threegroup.common.entity.OrderDetailEntity;
import com.threegroup.common.entity.OrdersEntity;
import com.threegroup.common.redis.RedisConf;
import com.threegroup.common.redis.RedisUtils;
import com.threegroup.common.utils.AssertUtil;
import com.threegroup.common.utils.OrderNumUtils;
import com.threegroup.common.utils.Result;
import com.threegroup.common.utils.UserUtils;
import com.threegroup.common.validator.AssertUtils;
import com.threegroup.web.annotation.Login;
import com.threegroup.web.dao.AddressBookEntityMapper;
import com.threegroup.web.dao.ShoppingCartEntityMapper;
import com.threegroup.web.pojo.AddressBookEntity;
import com.threegroup.web.pojo.ShoppingCartEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

@Service
public class OrdersService {
    @Resource
    private OrdersDao ordersDao;

    @Resource
    private OrderDetailService orderDetailService;

    @Resource
    private OrderDetailDao orderDetailDao;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private UserUtils userUtils;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private ShoppingCartEntityMapper shoppingCartEntityMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    //客户
    //@Resource
    //private UserDao userDao;

    @Resource
    private AddressBookEntityMapper addressBookEntityMapper;


    /**
     * 生成订单号(生成tonken用于实现订单的幂等性)
     * @return
     */
    public Result getOrdersNamber(String id ) {
        String number = OrderNumUtils.getOrderNum();
        //值为订单号,并当做token存入redis
        redisUtils.set(RedisConf.ORDERSNUMBER + RedisConf.SEGMENTATION + number, number, RedisUtils.HOUR_ONE_EXPIRE);
        Result result = new Result();
        result.setData(number);
        /**
         *同步数据库
         *
         */
        //准备redis模板
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();
        stringRedisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        //查询redis是否存在该信息
        List<ShoppingCartEntity> shoppingCartEntityList = new ArrayList<>();
        Map<String, String> entries = opsForHash.entries(id);//根据key获取所有的redis
        if (!entries.isEmpty()) {
            //遍历结果集
            for (String s1 : entries.keySet()) {
                String s = entries.get(s1);
                //获取每一个菜品信息
                ShoppingCartEntity shoppingCartEntity = JSON.parseObject(s, ShoppingCartEntity.class);
                //添加List集合
                if (shoppingCartEntity != null) {
                    shoppingCartEntityList.add(shoppingCartEntity);
                }
            }
            //查询list时，把查询到的所有菜品同一为购物车，并同步到数据库
            int i = shoppingCartEntityMapper.copyDataToDB(shoppingCartEntityList);


            }
            return result;
        }

    /**
    添加订单
        1.参数校验
            下单用户
                非空 用户存在
            地址id
                非空 该地址id存在
            下单时间
                空 设置默认值
                非空 合法
            订单号
                空 设置默认
                非空 合法
            订单状态
                空 设置默认为 1 待付款
                非空 合法
            结账时间
                空
                非空 合法
            支付方式
                空
                非空 合法
            实收金额
                空
                非空 合法
            备注
                空
                非空
            phone
                空 通过地址id 拿到phone
                非空  合法
            address
                空 通过地址id 拿到address
                非空  合法
            consignee
                空 通过地址id 拿到consignee
                非空  合法
            userName
                空 通过userId拿到userName
          2.执行添加操作

     **/
    @Transactional(propagation = Propagation.REQUIRED)

    public Result addOrders(OrdersDTO ordersDTO,Long userId) {

        Result result = new Result();

        //抽取数据
        String number = ordersDTO.getNumber();

        Integer status = ordersDTO.getStatus();
        //设置默认提交订单状态为已支付
        status = 1;

        //地址id需要从前端接收进行校验
        Long addressBookId = ordersDTO.getAddressBookId();
        //设置默认提交订单时间
        Date orderTime = ordersDTO.getOrderTime();
        orderTime=new Date();

        Date checkoutTime = ordersDTO.getCheckoutTime();
        Integer payMethod = ordersDTO.getPayMethod();
        BigDecimal amount = BigDecimal.ZERO;
        String remark = ordersDTO.getRemark();
        String phone = ordersDTO.getPhone();
        String address = ordersDTO.getAddress();
        String consignee = ordersDTO.getConsignee();
        String userName = ordersDTO.getUserName();
        //订单明细需要从购物车信息中获取
        List<ShoppingCartEntity> shoppingCartEntities = shoppingCartEntityMapper.queryByUserId(userId);
        List<OrderDetailDTO> orderDetails = new ArrayList<>();
        for (ShoppingCartEntity shoppingCartEntity : shoppingCartEntities) {
            OrderDetailDTO orderDetailDTO = new OrderDetailDTO();
            orderDetailDTO.setName(shoppingCartEntity.getName());
            orderDetailDTO.setNumber(shoppingCartEntity.getNumber());
            orderDetailDTO.setAmount(shoppingCartEntity.getAmount());
            orderDetailDTO.setDishFlavor(shoppingCartEntity.getDishFlavor());
            orderDetailDTO.setDishId(shoppingCartEntity.getDishId());
            orderDetailDTO.setImage(shoppingCartEntity.getImage());
            orderDetailDTO.setSetmealId(shoppingCartEntity.getSetmealId());
            orderDetails.add(orderDetailDTO);
        }

        /**
         * 订单号校验,非空
         */
        AssertUtils.isNull(number,"订单号为空");

        /**用户Id
            非空 存在
         **/
        AssertUtils.isNull(userId,"用户DI为空");
        //UserEntity userEntity = userDao.selectById(userId);
        //AssertUtils.isNull(userEntity,"该用户不存在");

        /**
         * userName
         *         空 通过userId拿到userName
         */
        //if (null==userName){
        //    userName=userEntity.getName();
        //}

        /**
         * 地址id
         *                 非空 该地址id存在
         */
        AssertUtils.isNull(addressBookId,"地址信息DI为空");
        AddressBookEntity addressBookEntity = addressBookEntityMapper.selectByPrimaryKey(addressBookId);
        AssertUtils.isNull(addressBookEntity,"该地址信息不存在");

        /**
         * phone
         *                 空 通过地址id 拿到phone
         *                 非空  合法
         */
        //空 通过地址id 拿到phone
        if (null==phone){
            phone=addressBookEntity.getPhone();
        }
        //非空  合法
        //AssertUtil.isTrue(!Pattern.matches("^1[3-9]\\d{9}$", phone),"手机号码格式有误");

        /**
         * address
         *                 空 通过地址id 拿到address
         *                 非空  合法
         */
        //address
        //空 通过地址id 拿到address
        if (null==address){
            address=addressBookEntity.getDetail();
        }
        //非空  合法

        /**
         * consignee
         *                 空 通过地址id 拿到consignee
         *                 非空  合法
         */
        //consignee
        //空 通过地址id 拿到consignee
        if (null==consignee){
            consignee=addressBookEntity.getConsignee();
        }
        //非空  合法

        /**
         * 下单时间
         *                 空 设置默认值
         *                 非空 合法
         */
        if (null==orderTime){
            orderTime=new Date();
        }

        /**
         * 订单状态
         *                 空 设置默认为 1 待付款
         *                 非空 合法
         */
        if (null==status){
            status=1;
        }
        AssertUtil.isTrue(!(status==1||status==2||status==3||status==4||status==5),"订单状态有误");

        /**
         * 结账时间
         *      空 非订单状态为1的不能为空
         *      非空 订单状态为结账时间需为空
         */
        if (null==checkoutTime){
            AssertUtil.isTrue(status!=1,"该订单已支付，结账时间不能为空");
        }

        /**
         * 状态设置为待支付
         */
        if (null==status){
            status = 1;
        }
        if (status==1){
            checkoutTime=null;
        }

        /**
         * 支付方式
         *                 空 支付状态为1可以为空，其他不合法
         *                 非空 合法
         */
        if (null==payMethod){
            AssertUtil.isTrue(status!=1,"已支付，无支付方式，不合法");
        }
        AssertUtil.isTrue(!(payMethod==1||payMethod==2),"支付方式有误");

        /**
         * 实收金额
         *      传入是否为空
         *          空 设置默认
         *          非空
         */
        if (amount.compareTo(BigDecimal.ZERO)==0){
            for (OrderDetailDTO orderDetail : orderDetails) {
                amount = amount.add(orderDetail.getAmount().multiply(new BigDecimal(orderDetail.getNumber())));
            }
        }
        //AssertUtil.isTrue(null==amount||amount.compareTo(BigDecimal.ZERO)<=0,"实收金额有误,不能为0和负数");


        OrdersEntity ordersEntity = new OrdersEntity();
        ordersEntity.setAddress(address);
        ordersEntity.setAmount(amount);
        ordersEntity.setOrderTime(orderTime);
        ordersEntity.setAddressBookId(addressBookId);
        ordersEntity.setCheckoutTime(checkoutTime);
        ordersEntity.setNumber(number);
        ordersEntity.setUserId(userId);
        ordersEntity.setPayMethod(payMethod);
        ordersEntity.setRemark(remark);
        ordersEntity.setPhone(phone);
        ordersEntity.setUserName(userName);
        ordersEntity.setConsignee(consignee);
        ordersEntity.setStatus(status);

        //定义订单id用于接收插入订单成功返回的主键,添加订单明细
        Long id;

        /**
         *  执行订单添加操作
         *      判断订单是否重复提交(幂等性)-----Token 获取，比较 和删除 必须是原子性
         *      redis.get（token），token.equals、redis.del（token）,如果说这两个操作都不是原子，可能导致，在高并发下，都 get 同样的数据，判断都成功，继续业务并发执行可以在 redis 使用 lua 脚本完成这个操作
         *      “if redis.call(‘get’,KEYS[1]) == ARGV[1] then return redis.call(‘del’,KEYS[1]) else return 0 end”
         *
         *          是
         *              返回订单超时或重复提交,请重试
         *          否
         *              删除ordersNumberToken保证订单的唯一性
         *              执行添加操作,把订单号存入redis 用来校验过期订单
         */
        //定义redis分布式锁保证Token 获取 比较 和 删除 是原子性,执行订单添加操作
        //防止其他线程误删
        String uuid = UUID.randomUUID().toString();
        //订单号
        String lockKey = "lock:coupon:"+number;

        Result result1 = lock(ordersEntity,lockKey,uuid);
        if (400==result1.getCode()){
            //返回添加失败的result
            return result1;
        }

        /**
         * 订单明细
         *      判断订单是否添加成功
         *          在订单添加成功的情况下添加订单明细
         *          订单明细需要从购物车信息获取
         */
        //获取从添加成功订单返回的到ordersEntity里面的id,用来绑定添加该订单的订单明细
        id = ordersEntity.getId();
        //判断订单是否添加成功
        if (null!=id){
            AssertUtils.isNull(orderDetails,"订单明细不能为空");
            AssertUtils.isListEmpty(orderDetails,"订单明细不能为空");
            for (OrderDetailDTO orderDetail : orderDetails) {
                orderDetailService.addOrderDetail(orderDetail, id);
            }
        }

        shoppingCartEntityMapper.deleteByUserId(String.valueOf(userId));

        return result;
    }

    /**
     * 给redis加锁判断对比删除token用lua脚本加分布式锁 ,执行添加订单方法(幂等性)
     */
    private Result lock(OrdersEntity ordersEntity ,String lockKey ,String uuid){

        //lua脚本
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
        //返回值
        Result result = new Result();
        //订单号
        String number = ordersEntity.getNumber();

        boolean nativeLock = redisTemplate.opsForValue().setIfAbsent(lockKey,uuid, Duration.ofSeconds(30));
        System.out.println("加锁状态:"+nativeLock);
        if (nativeLock){
            //加锁成功
            try {
                //测试分布式锁
                //TimeUnit.SECONDS.sleep(3);

                //定义获取ordersNumberToken 校验订单是否为同一个订单
                String ordersNumberToken = RedisConf.ORDERSNUMBER+RedisConf.SEGMENTATION+number;

                //判断订单是否重复提交(幂等性)---Token 获取，比较 和删除 必须是原子性
                if (number.equals(redisUtils.get(ordersNumberToken))){

                    //删除ordersNumberToken保证订单的唯一性
                     redisUtils.delete(ordersNumberToken);

                    //执行添加操作
                    int num = ordersDao.insertSelective(ordersEntity);
                    AssertUtil.isTrue(num!=1,"订单添加失败");

                    //将订单号存入redis 用来校验过期订单
                    redisUtils.set("ORDER:"+number,number,RedisUtils.HOUR_HALF_EXPIRE);

                }else {
                    result.setCode(400);
                    result.setMsg("订单提交超时或重复提交,请重试");
                }
            }catch (Exception e){
                System.out.println("异常=============================================="+e);
                result.setCode(400);
                result.setMsg("订单提交失败");
            }finally {
                //解锁
                Long result1 = (Long) redisTemplate.execute(new DefaultRedisScript<>(script,Long.class),Arrays.asList(lockKey),uuid);
                System.out.println("解锁状态:"+result1);
            }
        } else {
           //自旋操作
                try {
                    System.out.println("加锁失败,睡眠50毫秒,进行自旋");
                    TimeUnit.MILLISECONDS.sleep(5000);
                }catch (InterruptedException e){ }
                //睡眠一会在尝试获取数据
                lock(ordersEntity,lockKey,uuid);
        }
        return result;
    }


    /**
     * 删除订单
     *  查看待删除记录是否存在
     * @param ids
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public Result deleteOrders(Long[] ids) {
        AssertUtils.isNull(ids,"ids为空请重试");
        for (Long id : ids) {
            AssertUtils.isNull(ordersDao.selectByPrimaryKey(id),"待删除订单不存在");
            AssertUtil.isTrue(ordersDao.deleteByPrimaryKey(id)<1,"删除订单失败");
        }
        return new Result();
    }


    /** 修改订单
        1.参数校验
            订单id
                非空 存在
            下单用户
                空
                非空 用户存在
            地址id
                空
                非空 该地址id存在
            下单时间
                空
                非空 合法
            订单号
                空
                非空 合法
            订单状态
                空
                非空 合法
            结账时间
                空
                    状态非1
                        并且原来的订单结账时间为空，报错
                非空
                    状态为1
                        设置为null
                    状态非1
                        合法
            支付方式
                空
                    状态非1
                        并且原来的支付方式为空，报错
                非空
                    状态为1
                        设置为null
                    状态非1
                    合法
            实收金额
                 空
                    状态非1
                        并且原来的订单结账时间为空，报错
                非空
                    状态为1
                        设置为null
                    状态非1
                        合法
            备注
                空
                非空
            phone
                空 通过地址id 拿到phone
                非空  合法
            address
                空 通过地址id 拿到address
                非空  合法
            consignee
                空 通过地址id 拿到consignee
                非空  合法
            userName
                空 通过userId拿到userName
     2.执行更新操作

     **/
    @Transactional(propagation = Propagation.REQUIRED)
    public Result updeteOrders(OrdersDTO ordersDTO) {

        Result result = new Result();

        //抽取数据
        Long id = ordersDTO.getId();
        String number = ordersDTO.getNumber();
        Integer status = ordersDTO.getStatus();
        Long userId = ordersDTO.getUserId();
        Long addressBookId = ordersDTO.getAddressBookId();
        Date orderTime = ordersDTO.getOrderTime();
        Date checkoutTime = ordersDTO.getCheckoutTime();
        Integer payMethod = ordersDTO.getPayMethod();
        BigDecimal amount = ordersDTO.getAmount();
        String remark = ordersDTO.getRemark();
        String phone = ordersDTO.getPhone();
        String address = ordersDTO.getAddress();
        String consignee = ordersDTO.getConsignee();
        String userName = ordersDTO.getUserName();

        AssertUtils.isNull(null==id&&null==number,"待更新订单ID或订单号不能为空");
        //定义从数据查出来的未更新的数据
        OrdersEntity ordersTemp = new OrdersEntity();
        //判断是用什么修改订单信息
        if (null==id){
            /**
             * 订单号
             *                 空 设置默认
             *                 非空 合法
             */
            ordersTemp = ordersDao.selectByNumber(number);
            AssertUtils.isNull(ordersTemp,"待更新订单不存在");
            id = ordersTemp.getId();
        }else {
            ordersTemp = ordersDao.selectByPrimaryKey(id);
            AssertUtils.isNull(ordersTemp,"待更新订单不存在");
            number = ordersTemp.getNumber();
        }



        /**用户Id
         非空 存在
         **/
        //if (null==userId){
        //    userId=ordersTemp.getUserId();
        //}
        //UserEntity userEntity = userDao.selectById(userId);
        //AssertUtils.isNull(userEntity,"该用户不存在");

        /**
         * userName
         *         空 通过userId拿到userName
         */
        //if (null==userName){
        //    userName=userEntity.getName();
        //}

        /**
         * 地址id
         *                 非空 该地址id存在
         */
        //if (null==addressBookId){
        //    addressBookId=ordersTemp.getAddressBookId();
        //}
        //AddressBookEntity addressBookEntity = addressBookDao.selectById(addressBookId);
        //AssertUtils.isNull(addressBookEntity,"该地址信息不存在");

        /**
         * phone
         *                 空 通过地址id 拿到phone
         *                 非空  合法
         */
        //空 通过地址id 拿到phone
        //if (null==phone){
        //    phone=addressBookEntity.getPhone();
        //}
        //非空  合法

        /**
         * address
         *                 空 通过地址id 拿到address
         *                 非空  合法
         */
        //address
        //空 通过地址id 拿到address
        //if (null==address){
        //    address=addressBookEntity.getCityName()+addressBookEntity.getDistrictName()+addressBookEntity.getProvinceName()+addressBookEntity.getDetail();
        //}
        //非空  合法

        /**
         * consignee
         *                 空 通过地址id 拿到consignee
         *                 非空  合法
         */
        //consignee
        //空 通过地址id 拿到consignee
        //if (null==consignee){
        //    consignee=addressBookEntity.getConsignee();
        //}
        //非空  合法

        /**
         * 下单时间
         *                 空 设置默认值
         *                 非空 合法
         */
        if (null==orderTime){
            orderTime=ordersTemp.getOrderTime();
        }

        /**
         * 订单状态
         *                 空
         *                 非空 合法
         */
        AssertUtil.isTrue(ordersTemp.getStatus()==5,"该订单已经取消,无法操作");
        if (null==status){
            status=ordersTemp.getStatus();
        }
        AssertUtil.isTrue(!(status==1||status==2||status==3||status==4||status==5),"订单状态有误");

        /** 结账时间
         空
            状态非1
                并且原来的订单结账时间为空，报错
         非空
            状态为1
                设置为null
            状态非1
                合法
         */
        if (null==checkoutTime){
            checkoutTime = new Date();
        }
        if (status==1){
            checkoutTime=null;
        }

        /**
         * 支付方式
            空
                状态非1
                    并且原来的支付方式为空，报错
            非空
                状态为1
                    设置为null
                状态非1
                    合法
         */
        if (null==payMethod){
            if (null==ordersTemp.getPayMethod()){
                AssertUtil.isTrue(status!=1,"已支付，无支付方式，不合法");
            }else {
                payMethod=ordersTemp.getPayMethod();
            }
        }
        AssertUtil.isTrue(!(payMethod==1||payMethod==2),"支付方式有误");
        if (status==1){
            payMethod=null;
        }

        /**
         * orderDetails
         *
         */


        /**
         * 实收金额
            空
                状态非1
                    并且原来的订单结账时间为空，报错
            非空
                状态为1
                    设置为null
                状态非1
                    合法
         */
        if (null==amount){
            if (null==ordersTemp.getAmount()){
                AssertUtil.isTrue(status!=1,"该订单状态实收金额不能为空");
            }else {
                amount = ordersTemp.getAmount();
            }
        }
        if (status==1){
            amount=null;
        }

        if (null==remark){
            remark=ordersTemp.getRemark();
        }

        OrdersEntity ordersEntity = new OrdersEntity();
        ordersEntity.setId(id);
        ordersEntity.setAddress(address);
        ordersEntity.setAmount(amount);
        ordersEntity.setOrderTime(orderTime);
        ordersEntity.setAddressBookId(addressBookId);
        ordersEntity.setCheckoutTime(checkoutTime);
        ordersEntity.setNumber(number);
        ordersEntity.setUserId(userId);
        ordersEntity.setPayMethod(payMethod);
        ordersEntity.setRemark(remark);
        ordersEntity.setPhone(phone);
        ordersEntity.setUserName(userName);
        ordersEntity.setConsignee(consignee);
        ordersEntity.setStatus(status);

        AssertUtil.isTrue(ordersDao.updateByPrimaryKeySelective(ordersEntity)<1,"更新订单失败");
        return result;
    }


    /**
     *查询订单信息
     */
    public Result selectOrders(Long id) {

        Result result = new Result();

        AssertUtils.isNull(id,"订单Id为空");

        OrdersEntity ordersEntity = ordersDao.selectByPrimaryKey(id.longValue());

        result.setData(ordersEntity);

        return result;
    }


    /**
     * 分页查询订单
     * @return
     */
    public Result selectOrderDetailList(OrdersPageDTO ordersPageDTO) {
        //Integer pageSize, Integer pageNum, String order, String orderField
        Result result = new Result();

        Long userId = userUtils.getUserId();

        Integer pageSize = ordersPageDTO.getPageSize();
        Integer pageNum = ordersPageDTO.getPage();
        String order = ordersPageDTO.getOrder();
        String orderField = ordersPageDTO.getOrderField();

        //订单号
        //Long Id = ordersPageDTO.getId();

        OrdersQueryDTO ordersQueryDTO = new OrdersQueryDTO();


        // 只允许指定的排序字段和排序方式，防止SQL注入
        String[] orderByArr = {"amount","id","number","status","order_time","checkout_time","pay_method"};
        String orderByStr = "";
        if (StringUtils.isNotEmpty(orderField) && Arrays.asList(orderByArr).contains(orderField.toLowerCase())) {
            orderByStr = String.format("%s %s", orderField.toLowerCase(), "asc".equalsIgnoreCase(order) ? "asc" : "desc");
        } else {
            // 默认排序
            orderByStr = "order_time desc";
        }

        PageHelper.startPage(pageNum,pageSize, orderByStr);
        List<OrdersEntity> allOrdersEntities = ordersDao.selectAllByUserId(userId);
        PageInfo<OrdersEntity> ordersEntitiesPageInfo = new PageInfo<>(allOrdersEntities);

        //查出来的订单询环查询出每个订单的订单详情
        List<OrdersEntity> ordersEntities = ordersEntitiesPageInfo.getList();
        for (OrdersEntity ordersEntity : ordersEntities) {
            Long orderId = ordersEntity.getId();
            List<OrderDetailEntity> orderDetailEntities = orderDetailDao.selectByOrderId(orderId);
            ordersEntity.setOrderDetails(orderDetailEntities);
        }

        Map<String,Object> map = new HashMap<>();
        map.put("list",ordersEntities);
        map.put("pages",ordersEntitiesPageInfo.getPages());
        result.setData(map);

        return result;
    }


}
